using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace MSNClient
{
    public enum GroupTypes
    {
        Online,
        Offline
    }
    class ContactList : TreeView
    {
        Font groupFont = new Font(new FontFamily("Arial"), 8f, FontStyle.Bold);
        Font memberFont = new Font(new FontFamily("Arial"), 8f, FontStyle.Regular);
        Font tagFont = new Font(new FontFamily("Arial"), 8f, FontStyle.Regular);
        //Back color of selected Node, when windows is active
        Brush selectedNodeBackColor = Brushes.LightBlue;

        //Back color of selected Node, when windows is inactive
        Brush selectedNodeBackColor_inActive = Brushes.Silver;
        Brush nodeTextColor = Brushes.Black;
        public ContactList()
        {
            //Seting mode in which we must render all text by hand
            this.DrawMode = TreeViewDrawMode.OwnerDrawText;
            this.Indent = 0;
            this.ShowLines = false;
            this.FullRowSelect = true;
            this.BorderStyle = BorderStyle.FixedSingle;
            this.ShowPlusMinus = false;
            this.ShowNodeToolTips = true;

            this.Font = groupFont;
            ImageList imageList = new ImageList();
            imageList.Images.Add("blank", new Bitmap(1, 1));
            imageList.Images.Add("online", new Icon(GetType(), "user-online.ico"));
            imageList.Images.Add("offline", new Icon(GetType(), "user-offline.ico"));

            this.ImageList = imageList;
        }

        public Brush SelecteNodeBackColor
        {
            get { return this.selectedNodeBackColor; }
            set { this.selectedNodeBackColor = value; }
        }
        /// <summary>
        /// Set/Gets font for group
        /// </summary>
        public Font GroupFont
        {
            get { return this.groupFont; }
            set { this.groupFont = value; }
        }
        /// <summary>
        /// Sets/gets font for Group member (Users) 
        /// </summary>
        public Font MemberFont
        {
            get { return this.memberFont; }
            set { this.groupFont = value; }
        }
        public void AddGroupNode(GroupTypes groupType)
        {
            AddGroupNode(groupType, groupType.ToString());
        }
        public void AddGroupNode(GroupTypes groupType, String text)
        {
            this.Nodes.Add(groupType.ToString(), text).NodeFont = groupFont;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key">Account name</param>
        /// <param name="text"></param>
        /// <param name="groupName">Name of Group </param>
        /// 
        public void AddMemberNode(ContactInfo contactInfo, GroupTypes groupType)
        {
            //Calling main methond and pass tag name as empty string
            if (groupType == GroupTypes.Offline)
            {

            }
            AddMemberNode(contactInfo.passportName, contactInfo.displayName, UserStatus.Offline, "");

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="accountName"></param>
        /// <param name="displayName"></param>
        /// <param name="userStatus"></param>
        /// <param name="tagName"></param>
        public void AddMemberNode(String accountName, String displayName, UserStatus userStatus, String tagName)
        {
            String groupName = UserStatus.Offline.ToString();

            if (userStatus != UserStatus.Offline)
            {
                groupName = UserStatus.Online.ToString();
            }

            if (this.Nodes[groupName] != null)
            {
                if (this.Nodes.Find(accountName, true).Length > 0)
                {
                    this.Nodes.Remove(this.Nodes.Find(accountName, true)[0]);
                }
                this.Nodes[groupName].Nodes.Add(accountName, displayName, groupName.ToLower()).NodeFont = memberFont;
                this.Nodes[groupName].Nodes[accountName].Tag = tagName;
                this.Nodes[groupName].Nodes[accountName].ToolTipText = accountName+"\n"+userStatus.ToString();
            }
            if (this.Nodes[GroupTypes.Online.ToString()].Nodes.Count > 0)
            {
                this.Nodes[GroupTypes.Online.ToString()].Expand();
            }
        }
        public void ChangeMemberInfo(String accountName, String newDisplayName)
        {

        }
        //Changes Member Group or status
        public void ChangeMemberGroup(UserStatusInfo userStatusInfo)
        {
            TreeNode[] nodes = this.Nodes.Find(userStatusInfo.accountName, true);
            if (nodes.Length > 0)
            {
                AddMemberNode(userStatusInfo.accountName, userStatusInfo.displayName, userStatusInfo.userStatus, userStatusInfo.tagName);
            }
        }

        public void ClearGroups()
        {
            for (int i = 0; i < this.Nodes.Count; i++)
            {
                this.Nodes[i].Nodes.Clear();
            }
        }
        protected override void OnLostFocus(EventArgs e)
        {
            Invalidate();
            base.OnLostFocus(e);
        }
        protected override void OnDrawNode(DrawTreeNodeEventArgs e)
        {
            Point textLocation = new Point(e.Bounds.X + this.Indent, e.Bounds.Y);            
            // Draw the background for a selected node.
            if (e.Node.IsSelected)
            {
                // Draw the background of the selected node.
                e.Graphics.FillRectangle(selectedNodeBackColor, NodeBounds(e.Node));
                if((e.State & TreeNodeStates.Focused) == 0)
                {
                    // Draw the background of the selected node when windows is inactive
                    e.Graphics.FillRectangle(selectedNodeBackColor_inActive, NodeBounds(e.Node));
                }

                //Drawing images.
                //We must repaint images if selection line was painted.
                if (this.ImageList != null)
                {
                    //I don't know why here must be 2, but it must be for correct drawing :)
                    int x = textLocation.X - this.ImageList.ImageSize.Height-2;
                    //Drawing group image
                    if (e.Node.Parent == null)
                    {
                        e.Graphics.DrawImage(this.ImageList.Images[0], x, e.Bounds.Y);
                    }
                    else
                    {
                        e.Graphics.DrawImage(this.ImageList.Images[e.Node.Parent.Name.ToLower()], x, e.Bounds.Y);
                    }
                }

            }
            if (e.Node.IsVisible)
            {
                //Getting current font of node
                Font nodeFont = e.Node.NodeFont;
                if (nodeFont == null)
                {
                    nodeFont = this.Font;
                }
                //ClearType font
                e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
                // If a node tag is present, draw its string representation 
                // to the right of the label text.

                String displayName = e.Node.Text;
                if (e.Node.Tag != null && e.Node.Tag.ToString().Length > 0)
                {
                    displayName += " -";
                    //Draw displayName
                    TextRenderer.DrawText(e.Graphics, displayName, nodeFont, textLocation, Color.Black);

                    int textLength = TextRenderer.MeasureText(e.Graphics, displayName, nodeFont).Width - 5;
                    TextRenderer.DrawText(e.Graphics, e.Node.Tag.ToString(), tagFont, new Point(textLength + textLocation.X, textLocation.Y), Color.Gray);
                }
                else
                {
                    //Draw displayName
                    TextRenderer.DrawText(e.Graphics, displayName, nodeFont, textLocation, Color.Black);

                }
            }
            base.OnDrawNode(e);
        }

        // Returns the bounds of the specified node, including the region 
        // occupied by the node label and any node tag displayed.
        private Rectangle NodeBounds(TreeNode node)
        {
            // Set the return value to the normal node bounds.
            Rectangle bounds = node.Bounds;
            bounds.Width = this.Width;
            bounds.X = 0;
            if (node.Tag != null)
            {
                // Retrieve a Graphics object from the TreeView handle
                // and use it to calculate the display width of the tag.
                Graphics g = this.CreateGraphics();
                int tagWidth = (int)g.MeasureString(node.Tag.ToString(), tagFont).Width;

                // Adjust the node bounds using the calculated value.
                //bounds.Offset(tagWidth / 2, 0);
                bounds = Rectangle.Inflate(bounds, tagWidth, 0);
            }
            return bounds;
        }
        protected override void OnMouseDown(MouseEventArgs e)
        {
            TreeNode clickedNode = this.GetNodeAt(e.X, e.Y);
            if (clickedNode != null)
            {
                if (NodeBounds(clickedNode).Contains(e.X, e.Y))
                {
                    this.SelectedNode = clickedNode;
                }
            }
            base.OnMouseDown(e);
        }
    }
}
